<!DOCTYPE html>
<html>
<head>
    <title>Two-Lens System Ray Diagram</title>
    <style>
        body {
            font-family: sans-serif;
            text-align: center;
        }
        canvas {
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <h1>Ray Diagram for Two-Lens System</h1>
    <canvas id="physicsCanvas" width="900" height="500"></canvas>
    <div style="text-align:left; max-width:900px; margin:auto;">
        <h3>Calculations Summary:</h3>
        <ul>
            <li><b>Position of final image:</b> 25 cm from the converging lens (or 10 cm from the diverging lens).</li>
            <li><b>Size of final image:</b> 1.0 cm (inverted and real).</li>
        </ul>
        <h3>Ray Diagram Explanation:</h3>
        <p>The diagram shows two principal rays originating from a point on a distant object (off-axis).</p>
        <ol>
            <li><b>Ray 1 (Green):</b> This ray passes through the optical center of the first lens (converging) and is undeviated. It then strikes the second lens (diverging) and is refracted to pass through the final image point.</li>
            <li><b>Ray 2 (Blue):</b> This ray is chosen such that it passes through the front focal point of the first lens. After refracting through the first lens, it travels parallel to the principal axis. When this parallel ray strikes the second lens, it is refracted such that it appears to come from the focal point of the diverging lens.</li>
        </ol>
        <p>The point where these two rays intersect after passing through both lenses determines the location and size of the final, real, inverted image.</p>
    </div>

<script>
    const canvas = document.getElementById('physicsCanvas');
    const ctx = canvas.getContext('2d');

    // --- Configuration ---
    const scale = 18; // pixels per cm
    const x_offset = 150; // pixels from left edge to lens 1
    const y_axis = canvas.height / 2; // y-coordinate of the optical axis

    // Helper function to transform cm coordinates to canvas coordinates
    function T(x_cm, y_cm) {
        return {
            x: x_offset + x_cm * scale,
            y: y_axis - y_cm * scale
        };
    }

    // --- Drawing Functions ---
    function drawArrow(fromX, fromY, toX, toY, color = 'black') {
        const headlen = 10; // length of head in pixels
        const dx = toX - fromX;
        const dy = toY - fromY;
        const angle = Math.atan2(dy, dx);
        ctx.strokeStyle = color;
        ctx.fillStyle = color;
        ctx.beginPath();
        ctx.moveTo(fromX, fromY);
        ctx.lineTo(toX, toY);
        ctx.lineTo(toX - headlen * Math.cos(angle - Math.PI / 6), toY - headlen * Math.sin(angle - Math.PI / 6));
        ctx.moveTo(toX, toY);
        ctx.lineTo(toX - headlen * Math.cos(angle + Math.PI / 6), toY - headlen * Math.sin(angle + Math.PI / 6));
        ctx.stroke();
    }

    function drawLens(x_cm, type, height_cm = 6) {
        const pos = T(x_cm, 0);
        const lensHeight = height_cm * scale;
        const lensWidth = 0.8 * scale;

        ctx.strokeStyle = 'black';
        ctx.lineWidth = 2;
        ctx.beginPath();
        ctx.moveTo(pos.x, pos.y - lensHeight / 2);
        ctx.lineTo(pos.x, pos.y + lensHeight / 2);
        ctx.stroke();

        if (type === 'converging') {
            ctx.beginPath();
            ctx.moveTo(pos.x, pos.y - lensHeight / 2);
            ctx.quadraticCurveTo(pos.x + lensWidth, pos.y, pos.x, pos.y + lensHeight / 2);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(pos.x, pos.y - lensHeight / 2);
            ctx.quadraticCurveTo(pos.x - lensWidth, pos.y, pos.x, pos.y + lensHeight / 2);
            ctx.stroke();
        } else if (type === 'diverging') {
            ctx.beginPath();
            ctx.moveTo(pos.x, pos.y - lensHeight / 2);
            ctx.quadraticCurveTo(pos.x - lensWidth, pos.y, pos.x, pos.y + lensHeight / 2);
            ctx.stroke();
            ctx.beginPath();
            ctx.moveTo(pos.x, pos.y - lensHeight / 2);
            ctx.quadraticCurveTo(pos.x + lensWidth, pos.y, pos.x, pos.y + lensHeight / 2);
            ctx.stroke();
        }
    }

    function drawText(text, x_cm, y_cm, align = 'center', baseline = 'middle') {
        const pos = T(x_cm, y_cm);
        ctx.fillStyle = 'black';
        ctx.font = '16px Arial';
        ctx.textAlign = align;
        ctx.textBaseline = baseline;
        ctx.fillText(text, pos.x, pos.y);
    }
    
    function drawOriginalText() {
        const pos = T(-20, 5);
        ctx.strokeStyle = 'black';
        ctx.lineWidth = 2;
        ctx.strokeRect(pos.x - 50, pos.y - 40, 120, 80);
        
        ctx.font = 'bold 16px "Courier New", monospace';
        ctx.fillStyle = 'black';
        ctx.textAlign = 'center';
        drawText('object of', -20, 6.5, 'center', 'bottom');
        drawText('finite size at', -20, 5.5, 'center', 'bottom');
        drawText('"∞" on the', -20, 4.5, 'center', 'bottom');
        drawText('lens axis', -20, 3.5, 'center', 'bottom');
        
        const arrowStart = T(-13, 5.5);
        const arrowEnd = T(-15, 5.5);
        drawArrow(arrowStart.x, arrowStart.y, arrowEnd.x, arrowEnd.y);
    }


    // --- Main Drawing ---
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 1. Draw Axis
    ctx.strokeStyle = 'black';
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(0, y_axis);
    ctx.lineTo(canvas.width, y_axis);
    ctx.stroke();

    // 2. Draw Lenses and Labels
    drawLens(0, 'converging');
    drawText('f = 20 cm', 0, -5);

    drawLens(15, 'diverging');
    drawText('f = -10 cm', 15, -5);
    
    // Draw original text box
    drawOriginalText();
    
    // Draw distance marker
    const distPos = T(0, -4);
    const distPos2 = T(15, -4);
    ctx.beginPath();
    ctx.moveTo(distPos.x, distPos.y - 5);
    ctx.lineTo(distPos.x, distPos.y + 5);
    ctx.moveTo(distPos2.x, distPos2.y - 5);
    ctx.lineTo(distPos2.x, distPos2.y + 5);
    ctx.moveTo(distPos.x, distPos.y);
    ctx.lineTo(distPos2.x, distPos.y);
    ctx.stroke();
    drawText('15 cm', 7.5, -4.5);


    // --- Ray Tracing ---
    const h1 = -0.5; // height of intermediate image
    const f1 = 20;
    const tan_alpha = h1 / f1; // angle of incoming parallel rays
    const h2 = -1.0; // height of final image

    // Define key points in cm
    const L1_center = { x: 0, y: 0 };
    const L2_center = { x: 15, y: 0 };
    const I1_tip = { x: 20, y: h1 };
    const I2_tip = { x: 25, y: h2 };

    // --- Ray 1 (Green): Through L1 center ---
    const R1_start_x = -7;
    const R1_start_y = tan_alpha * R1_start_x;
    const R1_L1_hit = { x: 0, y: 0 }; // by definition
    const R1_L2_hit_y = tan_alpha * 15;
    const R1_L2_hit = { x: 15, y: R1_L2_hit_y };

    ctx.strokeStyle = '#008000'; // Green
    ctx.lineWidth = 1.5;
    
    // Incident ray
    let p_start = T(R1_start_x, R1_start_y);
    let p_l1 = T(R1_L1_hit.x, R1_L1_hit.y);
    ctx.beginPath();
    ctx.moveTo(p_start.x, p_start.y);
    ctx.lineTo(p_l1.x, p_l1.y);
    ctx.stroke();

    // Ray between lenses
    let p_l2_hit = T(R1_L2_hit.x, R1_L2_hit.y);
    ctx.beginPath();
    ctx.moveTo(p_l1.x, p_l1.y);
    ctx.lineTo(p_l2_hit.x, p_l2_hit.y);
    ctx.stroke();

    // Final ray
    let p_i2 = T(I2_tip.x, I2_tip.y);
    drawArrow(p_l2_hit.x, p_l2_hit.y, p_i2.x, p_i2.y, '#008000');


    // --- Ray 2 (Blue): Through L1 front focal point ---
    const F1_front = {x: -20, y: 0};
    const R2_L1_hit_y = tan_alpha * (0 - F1_front.x); // from similar triangles
    const R2_L1_hit = {x: 0, y: R2_L1_hit_y};
    const R2_L2_hit = {x: 15, y: R2_L1_hit_y};

    ctx.strokeStyle = '#0000FF'; // Blue
    
    // Incident ray
    const R2_start_x = -25;
    const R2_start_y = tan_alpha * (R2_start_x - F1_front.x);
    p_start = T(R2_start_x, R2_start_y);
    p_l1 = T(R2_L1_hit.x, R2_L1_hit.y);
    ctx.beginPath();
    ctx.moveTo(p_start.x, p_start.y);
    ctx.lineTo(p_l1.x, p_l1.y);
    ctx.stroke();

    // Ray between lenses (parallel to axis)
    p_l2_hit = T(R2_L2_hit.x, R2_L2_hit.y);
    ctx.beginPath();
    ctx.moveTo(p_l1.x, p_l1.y);
    ctx.lineTo(p_l2_hit.x, p_l2_hit.y);
    ctx.stroke();
    
    // Final ray
    drawArrow(p_l2_hit.x, p_l2_hit.y, p_i2.x, p_i2.y, '#0000FF');

    // --- Draw Images ---
    // Intermediate Image I1 (dashed)
    ctx.strokeStyle = '#FF8C00'; // Orange
    ctx.fillStyle = '#FF8C00';
    ctx.lineWidth = 1.5;
    ctx.setLineDash([5, 3]);
    let p_i1_base = T(I1_tip.x, 0);
    let p_i1_tip = T(I1_tip.x, I1_tip.y);
    drawArrow(p_i1_base.x, p_i1_base.y, p_i1_tip.x, p_i1_tip.y, '#FF8C00');
    drawText('I₁', I1_tip.x + 1, I1_tip.y, 'left');
    ctx.setLineDash([]); // Reset line dash

    // Final Image I2 (solid)
    ctx.strokeStyle = '#FF0000'; // Red
    ctx.fillStyle = '#FF0000';
    ctx.lineWidth = 2;
    let p_i2_base = T(I2_tip.x, 0);
    p_i2 = T(I2_tip.x, I2_tip.y);
    drawArrow(p_i2_base.x, p_i2_base.y, p_i2.x, p_i2.y, '#FF0000');
    drawText('I₂ (Final Image)', I2_tip.x + 1, I2_tip.y, 'left');
    
    // Figure Label
    ctx.font = '16px "Times New Roman"';
    ctx.fillStyle = 'black';
    drawText('Fig. 1.29 (with ray tracing)', 12, -12);

</script>

</body>
</html>